package redisPoolTool

import (
	"Gin-Server-01/com/server/common"
	"Gin-Server-01/com/server/core/logs"
	"Gin-Server-01/com/server/core/tools/yaml"
	"fmt"
	"github.com/go-redis/redis/v8"
	"golang.org/x/net/context"
	"log"
	"sync"
	"time"
)

/**
【OK@2023年8月26日】
 *下载依赖 : go get github.com/go-redis/redis/v8
 * @Author Administrator
 * @Description Redis连接池工具类
 * @Date 2023/7/7 17:28
 * @Version 2.1
*/

// 定义全局变量
var RedisPoolToolV8 *redis.Client
var once sync.Once

// init
//
//	@Description: 初始化Redis线程池
func init() {
	InitRedisClient()
}

// ZAdd 将指定成员及其分数添加到存储在键中的有序集合中。
// @param: key 键
// @param: membersAndScores 成员和分数的集合，依次为成员1，分数1，成员2，分数2，...
// @return int64
// @return error
func ZAdd(key string, membersAndScores ...*redis.Z) (int64, error) {
	result, err := RedisPoolToolV8.ZAdd(RedisPoolToolV8.Context(), key, membersAndScores...).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("添加元素到有序集合失败: %s", err))
		return 0, err
	}
	return result, nil
}

// ZRem 从存储在键中的有序集合中移除指定的成员。
// @param: key 键
// @param: members 成员列表
// @return int64
// @return error
func ZRem(key string, members ...interface{}) (int64, error) {
	result, err := RedisPoolToolV8.ZRem(RedisPoolToolV8.Context(), key, members...).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("从有序集合中删除元素失败: %s", err))
		return 0, err
	}
	return result, nil
}

// ZRange 返回存储在键中的有序集合中指定范围的元素。
// @param: key 键
// @param: start 起始索引
// @param: stop 结束索引
// @return []string
// @return error
func ZRange(key string, start, stop int64) ([]string, error) {
	result, err := RedisPoolToolV8.ZRange(RedisPoolToolV8.Context(), key, start, stop).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("获取有序集合指定范围的元素失败: %s", err))
		return nil, err
	}
	return result, nil
}

// GetKey
//
//	@Description: 获取Key对应的值
//	@param: key
//	@return string
//	@return error
func GetKey(key string) (string, error) {
	value, err := RedisPoolToolV8.Get(RedisPoolToolV8.Context(), key).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("获取Key对应的值失败:%s", err))
		return "", err
	}

	return value, nil
}

// DelKey
//
//	@Description: 删除指定的Key
//	@param: key
//	@return int64
//	@return error
func DelKey(key string) (int64, error) {
	result, err := RedisPoolToolV8.Del(RedisPoolToolV8.Context(), key).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("删除指定的Key失败:%s", err))
		return 0, err
	}

	return result, nil
}

// IncrBy
//
//	@Description: 将Key对应的值增加指定的增量
//	@param: key
//	@param: increment
//	@return int64
//	@return error
func IncrBy(key string, increment int64) (int64, error) {
	result, err := RedisPoolToolV8.IncrBy(RedisPoolToolV8.Context(), key, increment).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("将Key对应的值增加指定的增量失败:%s", err))
		return 0, err
	}

	return result, nil
}

// RedisDataType
// @Description: 常用Redis数据类型
type RedisDataType struct {
	client *redis.Client
}

// InitRedisClient
//
//	@Description:初始化Redis客户端
//	@return *redis.Client
func InitRedisClient() *redis.Client {
	if common.Configs == nil {
		// 读取配置文件
		common.Configs = yaml.ParseYAMLDefaultFileNoErr()
	}
	once.Do(func() {
		RedisPoolToolV8 = redis.NewClient(&redis.Options{
			Addr:     common.Configs.Redis.Address,  // Redis的地址和端口
			Password: common.Configs.Redis.Password, // Redis的密码，如果没有密码，可以设置为空字符串
			DB:       common.Configs.Redis.DbIndex,  // Redis的数据库索引，默认为0
			PoolSize: common.Configs.Redis.PoolSize, // 连接池的大小
		})

		// 检查连接是否成功
		pong, err := RedisPoolToolV8.Ping(RedisPoolToolV8.Context()).Result()
		if err != nil {
			logs.DaoPrintln(err.Error())
		}
		logs.DaoPrintln(pong)
	})

	return RedisPoolToolV8
}

// SetKeyWithExpire
//
//	@Description: 设置Key和过期时间
//	@param: key
//	@param: value
//	@param: expiration
//	@return error
func SetKeyWithExpire(key string, value interface{}, expiration time.Duration) error {
	err := RedisPoolToolV8.Set(RedisPoolToolV8.Context(), key, value, expiration).Err()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("设置Key失败:%s", err))
		return err
	}

	logs.DaoPrintln("Key设置成功")
	return nil
}

// SetNX
// @Description: SETNX分布式锁操作 - 当Key不存在时设置值
// @receiver r
// @param: key 设置的锁Key
// @param: value 设置的值
// @param: expiration 过期时间/times.func
// @return bool
// @return error
func SetNX(key string, value interface{}, expiration time.Duration) (bool, error) {
	result, err := RedisPoolToolV8.SetNX(RedisPoolToolV8.Context(), key, value, expiration).Result()
	if err != nil {
		return false, err
	}
	return result, nil
}

// LPush
//
// @Description: 将一个或多个值插入列表的左侧
// @param: key 键
// @param: values 值列表
// @return int64
// @return error
func LPush(key string, values ...interface{}) (int64, error) {
	result, err := RedisPoolToolV8.LPush(RedisPoolToolV8.Context(), key, values...).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("将值插入列表的左侧失败:%s", err))
		return 0, err
	}

	return result, nil
}

// LRemValue
//
//	@Description: 从列表中删除指定值
//	@param: key 指定的key
//	@param: value 删除的值
//	@return error
func LRemValue(key, value string) error {
	_, err := RedisPoolToolV8.LRem(RedisPoolToolV8.Context(), key, 0, value).Result()
	if err != nil {
		log.Printf("删除值失败：%s\n", err)
		return err
	}

	return nil
}

// LRangeValues 获取指定 key 的列表值
// @param: key 键
// @return []string
// @return error
func LRangeValues(key string) ([]string, error) {
	values, err := RedisPoolToolV8.LRange(RedisPoolToolV8.Context(), key, 0, -1).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("获取列表值失败：%s", err))
		return nil, err
	}

	return values, nil
}

// LPushWithExpiration 将一个或多个值插入列表的左侧，并设置过期时间
// @param: key 键
// @param: expiration 过期时间，单位为秒
// @param: values 值列表
// @return int64
// @return error
func LPushWithExpiration(key string, expiration time.Duration, values ...interface{}) (int64, error) {
	// LPush 插入值到列表的左侧
	result, err := RedisPoolToolV8.LPush(RedisPoolToolV8.Context(), key, values...).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("将值插入列表的左侧失败:%s", err))
		return 0, err
	}

	// 设置过期时间
	_, err = RedisPoolToolV8.Expire(RedisPoolToolV8.Context(), key, expiration).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("设置过期时间失败:%s", err))
		return 0, err
	}

	return result, nil
}

// RPop
//
// @Description: 移除并返回列表最右侧的元素
// @param: key 键
// @return string
// @return error
func RPop(key string) (string, error) {
	value, err := RedisPoolToolV8.RPop(RedisPoolToolV8.Context(), key).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("移除并返回列表最右侧的元素失败:%s", err))
		return "", err
	}

	return value, nil
}

// HSet
//
// @Description: 设置哈希字段的字符串值
// @param: key 键
// @param: field 哈希字段
// @param: value 字符串值
// @return bool
// @return error
func HSet(key string, field string, value string) (int64, error) {
	result, err := RedisPoolToolV8.HSet(RedisPoolToolV8.Context(), key, field, value).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("设置哈希字段的字符串值失败:%s", err))
		return 0, err
	}

	return result, nil
}

// HSetWithExpiration
// @Description: 设置哈希字段的字符串值
// @param: key 键
// @param: field 哈希字段
// @param: value 字符串值
// @param: expiration 过期时间
//
//	@return int64 影响的行数
//	@return error 错误
func HSetWithExpiration(key string, field string, value string, expiration time.Duration) (int64, error) {
	// 使用上下文来管理Redis命令的执行
	ctx, cancel := context.WithTimeout(context.Background(), expiration) // Adjust the timeout as needed
	defer cancel()

	// HSet
	result, err := RedisPoolToolV8.HSet(ctx, key, field, value).Result()
	if err != nil {
		return 0, err
	}

	if result == 0 {
		return result, nil
	}

	// 设置过期时间
	_, err = RedisPoolToolV8.Expire(ctx, key, expiration).Result()
	if err != nil {
		return 0, err
	}

	return result, nil
}

// HGetAll
//
//	@Description: 根据key值获取这个key的所有hash表
//	@param: key
//	@return map[string]string
//	@return error
func HGetAll(key string) (map[string]string, error) {
	result, err := RedisPoolToolV8.HGetAll(RedisPoolToolV8.Context(), key).Result()
	if err != nil {
		log.Printf("获取哈希表失败: %s", err)
		return nil, err
	}

	return result, nil
}

// HGet
//
// @Description: 获取哈希字段的字符串值
// @param: key 键
// @param: field 哈希字段
// @return string
// @return error
func HGet(key string, field string) (string, error) {
	value, err := RedisPoolToolV8.HGet(RedisPoolToolV8.Context(), key, field).Result()
	if err != nil {
		logs.DaoPrintln(fmt.Sprintf("获取哈希字段的字符串值失败:%s", err))
		return "", err
	}

	return value, nil
}
